package com.opencvjava.lessons.imgproc;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfInt;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static com.opencvjava.support.util.CvUtils.imshow;
import static com.opencvjava.support.util.CvUtils.mat;
import static org.opencv.core.Core.normalize;
import static org.opencv.core.Core.split;
import static org.opencv.imgproc.Imgproc.calcHist;
import static org.opencv.imgproc.Imgproc.line;

@Service
public class L18_HistogramCalc {
    public void test() {
        Mat src = mat("lena.jpg");
        if (src.empty()) {
            throw new RuntimeException("读取图片失败");
        }
        HistogramAxes(src);
    }

    public static void HistogramAxesGray(Mat src) {
        List<Mat> bgrPlanes = new ArrayList<>();
        split(src, bgrPlanes);
        int histSize = 256;
        float[] range = {0, 256}; //the upper boundary is exclusive
        MatOfFloat histRange = new MatOfFloat(range);
        Mat grayHist = new Mat();
        calcHist(bgrPlanes, new MatOfInt(0), new Mat(), grayHist, new MatOfInt(histSize), histRange, false);
        int histW = 512, histH = 400;
        int binW = (int) Math.round((double) histW / histSize);
        Mat histImage = new Mat(histH, histW, CvType.CV_8UC3, new Scalar(0, 0, 0));
        normalize(grayHist, grayHist, 0, histImage.rows(), Core.NORM_MINMAX);
        float[] grayHistData = new float[(int) (grayHist.total() * grayHist.channels())];
        grayHist.get(0, 0, grayHistData);
        for (int i = 1; i < histSize; i++) {
            line(histImage, new Point(binW * (i - 1), histH - Math.round(grayHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(grayHistData[i])), new Scalar(255, 0, 0), 2);
        }
        imshow("Source gray image", src);
        imshow("calcHist gray Demo", histImage);
    }

    public static void HistogramAxes(Mat src) {
        List<Mat> bgrPlanes = new ArrayList<>();
        split(src, bgrPlanes);
        int histSize = 256;
        float[] range = {0, 256}; //the upper boundary is exclusive
        MatOfFloat histRange = new MatOfFloat(range);
        boolean accumulate = false;
        Mat bHist = new Mat(), gHist = new Mat(), rHist = new Mat();
        calcHist(bgrPlanes, new MatOfInt(0), new Mat(), bHist, new MatOfInt(histSize), histRange, accumulate);
        calcHist(bgrPlanes, new MatOfInt(1), new Mat(), gHist, new MatOfInt(histSize), histRange, accumulate);
        calcHist(bgrPlanes, new MatOfInt(2), new Mat(), rHist, new MatOfInt(histSize), histRange, accumulate);
        int histW = 512, histH = 400;
        int binW = (int) Math.round((double) histW / histSize);
        Mat histImage = new Mat(histH, histW, CvType.CV_8UC3, new Scalar(0, 0, 0));
        normalize(bHist, bHist, 0, histImage.rows(), Core.NORM_MINMAX);
        normalize(gHist, gHist, 0, histImage.rows(), Core.NORM_MINMAX);
        normalize(rHist, rHist, 0, histImage.rows(), Core.NORM_MINMAX);
        float[] bHistData = new float[(int) (bHist.total() * bHist.channels())];
        bHist.get(0, 0, bHistData);
        float[] gHistData = new float[(int) (gHist.total() * gHist.channels())];
        gHist.get(0, 0, gHistData);
        float[] rHistData = new float[(int) (rHist.total() * rHist.channels())];
        rHist.get(0, 0, rHistData);
        for (int i = 1; i < histSize; i++) {
            line(histImage, new Point(binW * (i - 1), histH - Math.round(bHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(bHistData[i])), new Scalar(255, 0, 0), 2);
            line(histImage, new Point(binW * (i - 1), histH - Math.round(gHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(gHistData[i])), new Scalar(0, 255, 0), 2);
            line(histImage, new Point(binW * (i - 1), histH - Math.round(rHistData[i - 1])),
                    new Point(binW * (i), histH - Math.round(rHistData[i])), new Scalar(0, 0, 255), 2);
        }
        imshow("Source image", src);
        imshow("calcHist Demo", histImage);
    }
}
